package com.example.docmanagement.service;

import com.example.docmanagement.dto.request.DocumentCreateRequest;
import com.example.docmanagement.dto.request.DocumentQueryRequest;
import com.example.docmanagement.dto.request.DocumentUpdateRequest;
import com.example.docmanagement.dto.response.DocumentResponse;
import com.example.docmanagement.dto.response.PageResponse;
import com.example.docmanagement.entity.Category;
import com.example.docmanagement.entity.Document;
import com.example.docmanagement.exception.DuplicateResourceException;
import com.example.docmanagement.exception.ResourceNotFoundException;
import com.example.docmanagement.repository.CategoryRepository;
import com.example.docmanagement.repository.DocumentRepository;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 文档服务层
 * 处理文档相关的业务逻辑
 * 
 * 缓存策略：
 * - 文档详情：缓存 30 分钟
 * - 文档列表：缓存 10 分钟
 * - 更新/删除时清除相关缓存
 */
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@CacheConfig(cacheNames = "documents") // 默认缓存名称
public class DocumentService {

    private final DocumentRepository documentRepository;
    private final CategoryRepository categoryRepository;
    private final EntityManager entityManager;

    /**
     * 获取文档列表（支持分页、筛选、排序）
     * 优化：分两次查询避免 N+1 问题
     * 1. 先查询文档列表（数据库分页）
     * 2. 批量查询关联的分类信息
     * 缓存：10 分钟
     */
    @Cacheable(cacheNames = "documentList", key = "'list:' + #request.page + ':' + #request.pageSize + ':' + (#request.status != null ? #request.status : 'all') + ':' + (#request.categoryId != null ? #request.categoryId : 'all') + ':' + (#request.keyword != null ? #request.keyword : '') + ':' + #request.sortBy + ':' + #request.sortOrder")
    public PageResponse<DocumentResponse> getDocuments(DocumentQueryRequest request) {
        // 构建分页参数
        Sort sort = Sort.by(
                "desc".equalsIgnoreCase(request.getSortOrder()) ? Sort.Direction.DESC : Sort.Direction.ASC,
                request.getSortBy());
        Pageable pageable = PageRequest.of(request.getPage() - 1, request.getPageSize(), sort);

        // 第一次查询：查询文档列表（数据库分页）
        Page<Document> page = documentRepository.searchDocuments(
                request.getKeyword(),
                request.getCategoryId(),
                request.getStatus(),
                pageable);

        List<Document> documents = page.getContent();

        // 第二次查询：批量查询关联的分类信息（避免 N+1）
        if (!documents.isEmpty()) {
            Set<Long> categoryIds = documents.stream()
                    .map(doc -> doc.getCategory() != null ? doc.getCategory().getId() : null)
                    .filter(java.util.Objects::nonNull)
                    .collect(Collectors.toSet());

            if (!categoryIds.isEmpty()) {
                Map<Long, Category> categoryMap = categoryRepository.findAllById(categoryIds)
                        .stream()
                        .collect(Collectors.toMap(Category::getId, category -> category));

                // 手动设置分类关联
                documents.forEach(doc -> {
                    if (doc.getCategory() != null && doc.getCategory().getId() != null) {
                        doc.setCategory(categoryMap.get(doc.getCategory().getId()));
                    }
                });
            }
        }

        // 转换为 DTO
        List<DocumentResponse> documentResponses = documents.stream()
                .map(this::convertToResponse)
                .collect(Collectors.toList());

        // 构建分页响应
        return PageResponse.<DocumentResponse>builder()
                .data(documentResponses)
                .page(request.getPage())
                .pageSize(request.getPageSize())
                .total(page.getTotalElements())
                .totalPages(page.getTotalPages())
                .hasNext(page.hasNext())
                .hasPrevious(page.hasPrevious())
                .build();
    }

    /**
     * 根据 slug 获取文档详情
     * 缓存：30 分钟
     * 注意：浏览量更新不影响缓存，因为缓存的是响应 DTO，不包含实时浏览量
     */
    // sync=true 启用缓存击穿保护，但根据 Spring 规范，sync=true 时不能再使用 unless 条件
    // 这里 getDocumentBySlug 在找不到文档时会抛出异常而不是返回 null，因此可以安全去掉 unless
    @Cacheable(key = "#slug", sync = true)
    @Transactional // 需要写操作（增加浏览量）
    public DocumentResponse getDocumentBySlug(String slug) {
        // 使用 JOIN FETCH 避免 N+1 查询
        Document document = documentRepository.findBySlugWithCategory(slug)
                .orElseThrow(() -> new ResourceNotFoundException("文档", "slug", slug));

        // 增加浏览量（异步更新，不影响缓存）
        document.setViewCount(document.getViewCount() + 1);
        documentRepository.save(document);

        // 返回响应（缓存的是这个响应对象）
        return convertToResponse(document);
    }

    /**
     * 根据 ID 获取文档详情
     * 缓存：30 分钟
     */
    @Cacheable(key = "'id:' + #id", unless = "#result == null")
    public DocumentResponse getDocumentById(Long id) {
        Document document = documentRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("文档", "id", id));
        return convertToResponse(document);
    }

    /**
     * 创建文档
     * 清除列表缓存
     */
    @CacheEvict(cacheNames = "documentList", allEntries = true)
    @Transactional
    public DocumentResponse createDocument(DocumentCreateRequest request) {
        // 检查 slug 是否重复
        if (documentRepository.existsBySlug(request.getSlug())) {
            throw new DuplicateResourceException("slug", request.getSlug());
        }

        // 构建文档实体
        Document document = Document.builder()
                .title(request.getTitle())
                .slug(request.getSlug())
                .summary(request.getSummary())
                .content(request.getContent())
                .contentPath(request.getContentPath())
                .contentUrl(request.getContentUrl())
                .contentBase64(request.getContentBase64())
                .contentType(request.getContentType())
                .tags(request.getTags())
                .status(request.getStatus())
                .isPublic(request.getIsPublic())
                .language(request.getLanguage())
                .viewCount(0)
                .likeCount(0)
                .build();

        // 设置分类
        if (request.getCategoryId() != null) {
            Category category = categoryRepository.findById(request.getCategoryId())
                    .orElseThrow(() -> new ResourceNotFoundException("分类", "id", request.getCategoryId()));
            document.setCategory(category);

            // 更新分类的文档计数
            category.setDocCount(category.getDocCount() + 1);
            categoryRepository.save(category);
        }

        // 保存文档
        Document saved = documentRepository.save(document);
        return convertToResponse(saved);
    }

    /**
     * 更新文档
     * 清除文档详情缓存和列表缓存
     */
    @Caching(evict = {
            @CacheEvict(key = "'id:' + #id"),
            @CacheEvict(cacheNames = "documentList", allEntries = true)
    })
    @Transactional
    public DocumentResponse updateDocument(Long id, DocumentUpdateRequest request) {
        Document document = documentRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("文档", "id", id));

        // 检查 slug 是否重复（排除自己）
        if (request.getSlug() != null && !request.getSlug().equals(document.getSlug())) {
            if (documentRepository.existsBySlug(request.getSlug())) {
                throw new DuplicateResourceException("slug", request.getSlug());
            }
            document.setSlug(request.getSlug());
        }

        // 更新字段（只更新非空字段）
        if (request.getTitle() != null)
            document.setTitle(request.getTitle());
        if (request.getSummary() != null)
            document.setSummary(request.getSummary());
        if (request.getContent() != null)
            document.setContent(request.getContent());
        if (request.getContentPath() != null)
            document.setContentPath(request.getContentPath());
        if (request.getContentUrl() != null)
            document.setContentUrl(request.getContentUrl());
        if (request.getContentBase64() != null)
            document.setContentBase64(request.getContentBase64());
        if (request.getContentType() != null)
            document.setContentType(request.getContentType());
        if (request.getTags() != null)
            document.setTags(request.getTags());
        if (request.getStatus() != null)
            document.setStatus(request.getStatus());
        if (request.getIsPublic() != null)
            document.setIsPublic(request.getIsPublic());
        if (request.getLanguage() != null)
            document.setLanguage(request.getLanguage());

        // 更新分类
        if (request.getCategoryId() != null) {
            Category newCategory = categoryRepository.findById(request.getCategoryId())
                    .orElseThrow(() -> new ResourceNotFoundException("分类", "id", request.getCategoryId()));

            // 如果分类改变，更新文档计数
            if (document.getCategory() == null || !document.getCategory().getId().equals(newCategory.getId())) {
                if (document.getCategory() != null) {
                    Category oldCategory = document.getCategory();
                    oldCategory.setDocCount(Math.max(0, oldCategory.getDocCount() - 1));
                    categoryRepository.save(oldCategory);
                }
                newCategory.setDocCount(newCategory.getDocCount() + 1);
                categoryRepository.save(newCategory);
            }

            document.setCategory(newCategory);
        }

        Document updated = documentRepository.save(document);
        return convertToResponse(updated);
    }

    /**
     * 删除文档
     * 清除文档详情缓存和列表缓存
     */
    @Caching(evict = {
            @CacheEvict(key = "'id:' + #id"),
            @CacheEvict(cacheNames = "documentList", allEntries = true)
    })
    @Transactional
    public void deleteDocument(Long id) {
        Document document = documentRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("文档", "id", id));

        // 更新分类的文档计数
        if (document.getCategory() != null) {
            Category category = document.getCategory();
            category.setDocCount(Math.max(0, category.getDocCount() - 1));
            categoryRepository.save(category);
        }

        documentRepository.delete(document);
    }

    /**
     * 增加文档浏览量
     */
    @Transactional
    public void incrementViewCount(Long id) {
        Document document = documentRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("文档", "id", id));
        document.setViewCount(document.getViewCount() + 1);
        documentRepository.save(document);
    }

    /**
     * 批量创建文档（性能优化版本）
     * 使用批量插入，减少数据库交互次数
     * 
     * @param requests 文档创建请求列表
     * @return 创建的文档列表
     */
    @Transactional
    public List<DocumentResponse> batchCreateDocuments(List<DocumentCreateRequest> requests) {
        List<Document> documents = new ArrayList<>();

        // 批量构建文档实体
        for (DocumentCreateRequest request : requests) {
            // 检查 slug 是否重复
            if (documentRepository.existsBySlug(request.getSlug())) {
                throw new DuplicateResourceException("slug", request.getSlug());
            }

            // 构建文档实体
            Document document = Document.builder()
                    .title(request.getTitle())
                    .slug(request.getSlug())
                    .summary(request.getSummary())
                    .content(request.getContent())
                    .contentPath(request.getContentPath())
                    .contentUrl(request.getContentUrl())
                    .contentBase64(request.getContentBase64())
                    .contentType(request.getContentType())
                    .tags(request.getTags())
                    .status(request.getStatus())
                    .isPublic(request.getIsPublic())
                    .language(request.getLanguage())
                    .viewCount(0)
                    .likeCount(0)
                    .build();

            // 设置分类
            if (request.getCategoryId() != null) {
                Category category = categoryRepository.findById(request.getCategoryId())
                        .orElseThrow(() -> new ResourceNotFoundException("分类", "id", request.getCategoryId()));
                document.setCategory(category);
            }

            documents.add(document);
        }

        // 批量插入（Hibernate 批量操作）
        List<DocumentResponse> responses = new ArrayList<>();
        for (int i = 0; i < documents.size(); i++) {
            Document document = documents.get(i);
            entityManager.persist(document);

            // 每 20 条刷新一次，避免内存溢出
            if (i % 20 == 0) {
                entityManager.flush();
                entityManager.clear();
            }

            // 转换前需要重新加载（因为 clear 后实体已分离）
            if (i % 20 == 19 || i == documents.size() - 1) {
                entityManager.flush();
                // 重新加载已保存的文档
                for (int j = Math.max(0, i - 19); j <= i; j++) {
                    Document saved = entityManager.find(Document.class, documents.get(j).getId());
                    if (saved != null) {
                        responses.add(convertToResponse(saved));
                    }
                }
                entityManager.clear();
            }
        }

        // 更新分类的文档计数
        documents.stream()
                .filter(d -> d.getCategory() != null)
                .collect(Collectors.groupingBy(Document::getCategory))
                .forEach((category, docs) -> {
                    category.setDocCount(category.getDocCount() + docs.size());
                    categoryRepository.save(category);
                });

        return responses;
    }

    /**
     * 转换为响应 DTO
     */
    private DocumentResponse convertToResponse(Document document) {
        return DocumentResponse.builder()
                .id(document.getId())
                .title(document.getTitle())
                .slug(document.getSlug())
                .summary(document.getSummary())
                .content(document.getContent())
                .contentHtml(document.getContentHtml())
                .contentPath(document.getContentPath())
                .contentUrl(document.getContentUrl())
                .contentBase64(document.getContentBase64())
                .contentType(document.getContentType())
                .categoryId(document.getCategory() != null ? document.getCategory().getId() : null)
                .categoryName(document.getCategory() != null ? document.getCategory().getName() : null)
                .tags(document.getTags())
                .status(document.getStatus())
                .isPublic(document.getIsPublic())
                .version(document.getVersion())
                .language(document.getLanguage())
                .authorId(document.getAuthorId())
                .author(document.getAuthorName())
                .viewCount(document.getViewCount())
                .likeCount(document.getLikeCount())
                .publishedAt(document.getPublishedAt())
                .createdAt(document.getCreatedAt())
                .updatedAt(document.getUpdatedAt())
                .build();
    }
}
